package primitive3D;

import static rayCaster.ApplicationInterface.*;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.vecmath.Point3d;

import rayCaster.Ray;

public class Pyramid extends Primitive3D {
	private double height;
	private double baseSide;
	private Point3d apex;
	private Point3d[] bottomPoints;
	
	/*
	 * By default the piramid base will be on the X-Y plane centered aroung de Z axis
	 * The baseside will be 1 and the height will be 1. All these values can be
	 * changed at the LibInterface
	 */
	public Pyramid(){
		name = new String("");
		color = PYRAMID_COLOR;
		position = new Point3d(PYRAMID_POSITION);
		height = PYRAMID_HEIGHT;
		baseSide = PYRAMID_SIDE;
		apex = new Point3d(PYRAMID_APEX);
		components = new ArrayList<Primitive3D>();
		/*
		 * Similar approach as the Cube constructor. We know that all the 
		 * points in the base meet:
		 * x = 1/2  or  x = - 1/2
		 * y = 1/2  or  y = - 1/2
		 * z = 0    
		 * So, we will generate the 4 values making combinations 
		 */
		double[] possibleValues = {baseSide/2, -baseSide/2};
		int len = possibleValues.length;		
		
		bottomPoints = new Point3d[len*len];
		/*
		int index = 0;
		for( int i=0 ; i < len ; i++ ){
			for( int j=0 ; j < len ; j++ ){				
				bottomPoints[index++] = new Point3d(possibleValues[i], possibleValues[j], 0);
			}
		}
		*/
		bottomPoints[0]=new Point3d(possibleValues[0],possibleValues[0], 0 );
		bottomPoints[1]=new Point3d(possibleValues[1],possibleValues[0], 0 );
		bottomPoints[2]=new Point3d(possibleValues[1],possibleValues[1], 0 );
		bottomPoints[3]=new Point3d(possibleValues[0],possibleValues[1], 0 );
		
		//We create the bottom Quadrilateral
		Quadrilateral q = new Quadrilateral(bottomPoints);
		q.setColor(PYRAMID_COLOR);
		components.add(q);
		
		/* Now create 4 Triangles. One vertices is common for all: the apex
		 * The other vertices are calculated in a similar way as we do for
		 * the cube. We create a Triangle only for every pair of adjacent
		 * vertices
		 */		
		len = bottomPoints.length;
		/*
		for( int i=0 ; i < len ; i++ ){
			for( int j=i+1 ; j < len ; j++ ){				
				if( areAdjacentPointsInTheBase( bottomPoints[i],bottomPoints[j] ) ){
					Triangle t = new Triangle(bottomPoints[i],bottomPoints[j], apex);
					t.setColor(LibInterface.PYRAMID_COLOR);
					components.add(t);
				}						
			}
		}	
		*/	
		for( int i= 0 ; i<len ; i++){
			Triangle t = new Triangle(bottomPoints[i],bottomPoints[(i+1)%4], apex);
			t.setColor(PYRAMID_COLOR);
			components.add(t);
		}
				
	}
	
	public void setHeight(double height){
		this.height = height;
	}
	public double getHeight(){
		return height;
	}
	
	public void scale(double scaleFactor){	
		scaleFromReferencePosition(this.position, scaleFactor);
	}
	@Override
	public void scaleFromReferencePosition(Point3d referencePosition, double scaleFactor) {
		for( int i=0 ; i < components.size() ; i++ ){
			components.get(i).scaleFromReferencePosition(referencePosition, scaleFactor);
		}
		for( int i=0 ; i < bottomPoints.length ; i++ ){			
			scalePointfromReferencePosition(bottomPoints[i], referencePosition, scaleFactor);
		}			
		baseSide = baseSide*scaleFactor;
		height = height*scaleFactor;
		scalePointfromReferencePosition(apex, referencePosition, scaleFactor);
	}

	@Override
	public void setPosition(Point3d otherPosition) {
		Point3d componentPosition;
		Point3d delta = new Point3d();		
		
		for(int i=0 ; i < components.size() ; i++ ){
			componentPosition = components.get(i).getPosition();			
			delta.sub(otherPosition, this.position);			
			componentPosition.add(delta);			
			components.get(i).setPosition(componentPosition);
		}
		for( int i=0 ; i < bottomPoints.length ; i++ ){			
			bottomPoints[i].sub(this.position);
			bottomPoints[i].add(otherPosition);
		}		
		apex.sub(this.position);
		apex.add(otherPosition);
		position = new Point3d(otherPosition);
	}

	@Override
	public String toString() {
		StringBuffer str = new StringBuffer();
		StringBuffer vertexStr = new StringBuffer();
		
		for( int i=0 ; i< bottomPoints.length ; i++ ){
			vertexStr.append(bottomPoints[i].toString() + "\n");
		}	
		
		str.append("Piramid \"" + getName() + "\"\nVertices = \n" + vertexStr + "Apex = " + apex + "\nPosition = " + position + "\nColor = " + color);
		return str.toString();
	}
	
	@SuppressWarnings("unused")
	private boolean areAdjacentPointsInTheBase(Point3d p1, Point3d p2) {				
		return Double.compare(p1.distance(p2), baseSide) ==  0;
	}
	
	
	// Devuelve la cantidad de puntos intersectados en el nombre de
	// la funcion y los puntos en el arreglo pts de longitud 2.  
	public int intersects(Ray ray, Point3d[] pts) throws Exception {
		Set<Point3d> ptsAux = new HashSet<Point3d>();
		for( Primitive3D aPrimitive : components ) {		
			if( aPrimitive.intersects(ray, pts) == 1 ) {
				ptsAux.add( new Point3d(pts[0]) );
			}
		}
		
		Iterator<Point3d> it = ptsAux.iterator();

		int cantIntersections = 0;
		if( it.hasNext() ) {
			pts[0] = it.next();
			cantIntersections++;
		}
		while( it.hasNext()) {
			Point3d p = it.next();
			if( !p.epsilonEquals(pts[0], 0.001) ) {				
				pts[1] = p;
				cantIntersections++;
				break;
			}
		}
		return cantIntersections;
		/*
		Iterator<Point3d> it = ptsAux.iterator();		
		for( int i=0; it.hasNext(); i++ ) {
			pts[i] = it.next();
		}
		return ptsAux.size();
		*/
	}

}
